001 /* 002 * Copyright 2003-2005 The Apache Software Foundation 003 * Copyright 2005 Stephen McConnell 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package net.dpml.cli.validation; 018 019 import java.text.DateFormat; 020 import java.text.ParsePosition; 021 022 import java.util.Date; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.ListIterator; 026 027 import net.dpml.cli.resource.ResourceConstants; 028 import net.dpml.cli.resource.ResourceHelper; 029 030 /** 031 * The <code>DateValidator</code> validates the argument values 032 * are date or time value(s). 033 * 034 * The following example shows how to validate that 035 * an argument value(s) is a Date of the following 036 * type: d/M/yy (see {@link java.text.DateFormat}). 037 * 038 * <pre> 039 * DateFormat date = new SimpleDateFormat("d/M/yy"); 040 * ... 041 * ArgumentBuilder builder = new ArgumentBuilder(); 042 * Argument dateFormat = 043 * builder.withName("date"); 044 * .withValidator(new DateValidator(dateFormat)); 045 * </pre> 046 * 047 * The following example shows how to validate that 048 * an argument value(s) is a time of the following 049 * type: HH:mm:ss (see {@link java.text.DateFormat}). 050 * 051 * <pre> 052 * DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); 053 * ... 054 * ArgumentBuilder builder = new ArgumentBuilder(); 055 * Argument time = 056 * builder.withName("time"); 057 * .withValidator(new DateValidator(timeFormat)); 058 * </pre> 059 * 060 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 061 * @version 1.0.0 062 * @see java.text.DateFormat 063 */ 064 public class DateValidator implements Validator 065 { 066 /** i18n */ 067 private static final ResourceHelper RESOURCES = 068 ResourceHelper.getResourceHelper(); 069 070 /** an array of permitted DateFormats */ 071 private DateFormat[] m_formats; 072 073 /** minimum Date allowed i.e: a valid date occurs later than this date */ 074 private Date m_minimum; 075 076 /** maximum Date allowed i.e: a valid date occurs earlier than this date */ 077 private Date m_maximum; 078 079 /** leniant parsing */ 080 private boolean m_isLenient; 081 082 /** 083 * Creates a Validator for the default date/time format 084 */ 085 public DateValidator() 086 { 087 this( DateFormat.getInstance() ); 088 } 089 090 /** 091 * Creates a Validator for the specified DateFormat. 092 * 093 * @param format 094 * a DateFormat which dates must conform to 095 */ 096 public DateValidator( final DateFormat format ) 097 { 098 setFormat( format ); 099 } 100 101 /** 102 * Creates a Validator for the List of specified DateFormats. 103 * 104 * @param formats a List of DateFormats which dates must conform to 105 */ 106 public DateValidator( final List formats ) 107 { 108 for( Iterator iter = formats.iterator(); iter.hasNext();) 109 { 110 DateFormat format = (DateFormat) iter.next(); 111 } 112 setFormats( formats ); 113 } 114 115 /** 116 * Creates a Validator for dates. 117 * 118 * @return DateValidator a Validator for dates 119 */ 120 public static DateValidator getDateInstance() 121 { 122 return new DateValidator( DateFormat.getDateInstance() ); 123 } 124 125 /** 126 * Creates a Validator for times. 127 * 128 * @return DateValidator a Validator for times 129 */ 130 public static DateValidator getTimeInstance() 131 { 132 return new DateValidator( DateFormat.getTimeInstance() ); 133 } 134 135 /** 136 * Creates a Validator for date/times 137 * 138 * @return DateValidator a Validator for date/times 139 */ 140 public static DateValidator getDateTimeInstance() 141 { 142 return new DateValidator( DateFormat.getDateTimeInstance() ); 143 } 144 145 /** 146 * Validate each String value in the specified List against this instances 147 * permitted DateFormats. 148 * 149 * If a value is valid then it's <code>String</code> value in the list is 150 * replaced with it's <code>Date</code> value. 151 * 152 * @param values the list of values to validate 153 * @exception InvalidArgumentException if a value is invalid 154 * @see net.dpml.cli.validation.Validator#validate(java.util.List) 155 */ 156 public void validate( final List values ) throws InvalidArgumentException 157 { 158 // for each value 159 for( final ListIterator i = values.listIterator(); i.hasNext();) 160 { 161 final Object next = i.next(); 162 if( next instanceof Date ) 163 { 164 return; 165 } 166 167 final String value = (String) next; 168 169 Date date = null; 170 171 // create a resuable ParsePosition instance 172 final ParsePosition pp = new ParsePosition( 0 ); 173 174 // for each permitted DateFormat 175 for( int f=0; ( f<m_formats.length ) && ( date == null ); ++f ) 176 { 177 // reset the parse position 178 pp.setIndex( 0 ); 179 date = m_formats[f].parse( value, pp ); 180 181 // if the wrong number of characters have been parsed 182 if( pp.getIndex() < value.length() ) 183 { 184 date = null; 185 } 186 } 187 188 // if date has not been set throw an InvalidArgumentException 189 if( date == null ) 190 { 191 throw new InvalidArgumentException( value ); 192 } 193 194 // if the date is outside the bounds 195 if( isDateEarlier( date ) || isDateLater( date ) ) 196 { 197 throw new InvalidArgumentException( 198 RESOURCES.getMessage( 199 ResourceConstants.DATEVALIDATOR_DATE_OUTOFRANGE, 200 value ) ); 201 } 202 203 // replace the value in the list with the actual Date 204 i.set( date ); 205 } 206 } 207 208 /** 209 * Set the leaniant flag. 210 * @param lenient true if leniant 211 */ 212 public void setLeniant( final boolean lenient ) 213 { 214 for( int i=0; i<m_formats.length; i++ ) 215 { 216 m_formats[i].setLenient( lenient ); 217 } 218 m_isLenient = lenient; 219 } 220 221 /** 222 * Return the leaniant flag. 223 * @return true if leniant 224 */ 225 public boolean isLeniant() 226 { 227 return m_isLenient; 228 } 229 230 /** 231 * Returns the maximum date permitted. 232 * 233 * @return Date the maximum date permitted. If no maximum date has been 234 * specified then return <code>null</code>. 235 */ 236 public Date getMaximum() 237 { 238 return m_maximum; 239 } 240 241 /** 242 * Sets the maximum Date to the specified value. 243 * 244 * @param maximum 245 * the maximum Date permitted 246 */ 247 public void setMaximum( final Date maximum ) 248 { 249 m_maximum = maximum; 250 } 251 252 /** 253 * Returns the minimum date permitted. 254 * 255 * @return Date the minimum date permitted. If no minimum date has been 256 * specified then return <code>null</code>. 257 */ 258 public Date getMinimum() 259 { 260 return m_minimum; 261 } 262 263 /** 264 * Sets the minimum Date to the specified value. 265 * 266 * @param minimum 267 * the minimum Date permitted 268 */ 269 public void setMinimum( Date minimum ) 270 { 271 m_minimum = minimum; 272 } 273 274 /** 275 * Returns whether the specified Date is later than the maximum date. 276 * 277 * @param date 278 * the Date to evaluate 279 * 280 * @return boolean whether <code>date</code> is earlier than the maximum 281 * date 282 */ 283 private boolean isDateLater( Date date ) 284 { 285 return ( m_maximum != null ) && ( date.getTime() > m_maximum.getTime() ); 286 } 287 288 /** 289 * Returns whether the specified Date is earlier than the minimum date. 290 * 291 * @param date the Date to evaluate 292 * @return boolean whether <code>date</code> is earlier than the minimum 293 * date 294 */ 295 private boolean isDateEarlier( Date date ) 296 { 297 return ( m_minimum != null ) && ( date.getTime() < m_minimum.getTime() ); 298 } 299 300 /** 301 * Sets the date format permitted. 302 * 303 * @param format 304 * the format to use 305 */ 306 public void setFormat( final DateFormat format ) 307 { 308 setFormats( new DateFormat[]{format} ); 309 } 310 311 /** 312 * Sets the date formats permitted. 313 * 314 * @param formats 315 * the List of DateFormats to use 316 */ 317 public void setFormats( final List formats ) 318 { 319 setFormats( (DateFormat[]) formats.toArray( new DateFormat[formats.size()] ) ); 320 } 321 322 /** 323 * Sets the date formats permitted. 324 * 325 * @param formats the array of DateFormats to use 326 */ 327 public void setFormats( final DateFormat[] formats ) 328 { 329 m_formats = formats; 330 setLeniant( m_isLenient ); 331 } 332 333 /** 334 * Gets the date formats permitted. 335 * 336 * @return the permitted formats 337 */ 338 public DateFormat[] getFormats() 339 { 340 return m_formats; 341 } 342 }